﻿using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace Heyo.Class.Helper.Network
{

    public class FileDownloader
    {
        public enum DownloadStatus
        {
            NotStarted,
            Downloading,
            Success,
            Timeout,
            CantWrite,
        }
        public FileDownloader(string remoteUri, string path)
        {
            LocalPath = path;
            RemoteUri = remoteUri;
            Status = DownloadStatus.NotStarted;
        }

        public string LocalPath { get; set; }
        public string RemoteUri { get; set; }
        public event Action<FileDownloader> OnCompleted;
        public event Action<FileDownloader> OnProgressChanged;
        public DownloadStatus Status { get; set; }
        public double Progress { get; private set; }
        public double Speed { get; private set; }
        public double TotalBytes { get; private set; }
        /// <summary>
        ///     下载文件，同时返回结果
        /// </summary>
        public DownloadStatus Download()
        {
            try
            {
                using (WebClient client = new WebClient())
                {
                    client.Headers.Add("user-agent", UAHelper.GenerateUa());
                    Status = DownloadStatus.Downloading;
                    byte[] result = client.DownloadData(RemoteUri);
                    using (MemoryStream ms = new MemoryStream(result))
                    {
                        Status= FileHelper.SaveBinaryFile(ms, LocalPath) ?
                            DownloadStatus.Success : DownloadStatus.CantWrite;
                    }
                }
            }
            catch (WebException ex)
            {
                Status = DownloadStatus.Timeout;
            }

            Progress = 1;
            Speed = double.PositiveInfinity;
            OnProgressChanged?.Invoke(this);
            OnCompleted?.Invoke(this);

            return Status;

        }

        public async Task<DownloadStatus> BeginDownload(CancellationTokenSource tokenSource = null)
        {
            try
            {
                using (WebClient client = new WebClient())
                {
                    client.Headers.Add("user-agent", UAHelper.GenerateUa());
                    client.DownloadDataCompleted += (_, e) =>
                    {
                        Speed = 0;
                        try
                        {
                            if (e.Result == null)
                            {
                                Status = DownloadStatus.Timeout;
                                Console.WriteLine(e.Error);
                                return;
                            }
                            using (MemoryStream ms = new MemoryStream(e.Result))
                            {
                                Status = FileHelper.SaveBinaryFile(ms, LocalPath) ?
                                    DownloadStatus.Success : DownloadStatus.CantWrite;
                            }
                            OnCompleted?.Invoke(this);
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine(exception);
                        }

                    };
                    long lastBytes=0, lastTime=DateTime.Now.Ticks;
                    client.DownloadProgressChanged += (_, e) =>
                    {
                        Progress = e.ProgressPercentage;
                        if (DateTime.Now.Ticks - lastTime > 1000)
                        {
                            Speed = (e.BytesReceived - lastBytes)/(double)(DateTime.Now.Ticks - lastTime)*1000;
                            lastTime = DateTime.Now.Ticks;
                            lastBytes = e.BytesReceived;
                        }

                        OnProgressChanged?.Invoke(this);
                        
                    };
                    Status = DownloadStatus.Downloading;
                    client.DownloadDataAsync(new Uri(RemoteUri));

                    await Task.Factory.StartNew(() =>
                    {
                        while (Status == DownloadStatus.Downloading)
                        {
                            Thread.Sleep(1000);
                            if (tokenSource?.Token.IsCancellationRequested != true) continue;
                            client.CancelAsync();
                            client.Dispose();

                        }
                    });
                }
            }
            catch (WebException)
            {
                Status = DownloadStatus.Timeout;
            }

            return Status;
        }
    }
}
